﻿using System.Collections.Generic;
using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Users;
using NailsFramework.Persistence;
using CID.Domain.Audit;
using CID.Domain.Development;

namespace CID.Domain.Operations
{
    public class Environment : Model<Environment>, IEnvironment
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
        public virtual bool Enabled { get; set; }
        public virtual bool IsProduction { get; set; }
        public virtual IUser Responsible { get; set; }

        private IList<IServerAssignment> assignments = new List<IServerAssignment>();
        public virtual IEnumerable<IServerAssignment> Assignments { get { return assignments; } }

        private IList<IActiveVersion> activeVersions = new List<IActiveVersion>();
        public virtual IEnumerable<IActiveVersion> ActiveVersions { get { return activeVersions; } }

        protected Environment()
        {
            Enabled = true;
            IsProduction = false;
        }

        public Environment(string name, string description, IUser responsible) : this ()
        {
            Name = name;
            Description = description;
            Responsible = responsible;
        }

        #region Equals and GetHashCode
        //This class needs to have the Equals overriden to support the comparison with the proxies from the persistence layer
        public override bool Equals(object obj)
        {
            return EntityEqualityHelper.Equals(this, obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        #endregion

        public virtual void Assign(IServer server, IServerRole serverRole)
        {
            var assignment = new ServerAssignment(server, serverRole, this);
            AddAssignment(assignment);
            server.AddAssignment(assignment);
            serverRole.AddAssignment(assignment);
        }

        public virtual void Unassign(IServer server, IServerRole serverRole)
        {
            var assignment = Assignments.FirstOrDefault(x => (x.Server.Equals(server)) && (x.ServerRole.Equals(serverRole)));
            if (assignment != null)
            {
                RemoveAssignment(assignment);
                server.RemoveAssignment(assignment);
                serverRole.RemoveAssignment(assignment);
            }
        }

        public virtual void AddAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.Environment)) throw new InvalidAssignmentException(this);

            if (Assignments.Count(x => (x.Server.Equals(assignment.Server)) && (x.ServerRole.Equals(assignment.ServerRole))) > 0)
                throw new DuplicateAssignmentException(this, assignment.Server, assignment.ServerRole);

            assignments.Add(assignment);
        }

        public virtual void RemoveAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.Environment)) throw new InvalidAssignmentException(this);

            if (assignments.Contains(assignment)) assignments.Remove(assignment);
        }

        public virtual void RegisterReleaseDeployment(IRelease release)
        {
            if (release.Status != ReleaseStatus.Active) throw new InvalidActionForReleaseStatusException("register release deployment in an environment", release.Status);

            foreach (IVersion version in release.Versions)
            {
                var activeVersion = GetActiveVersion(version);
                if (activeVersion == null)
                {
                    activeVersion = GetActiveVersion(version.Component);
                    if (activeVersion != null)
                        activeVersions.Remove(activeVersion);

                    activeVersions.Add(new ActiveVersion(this, version));
                }
            }
        }

        public virtual void UnregisterComponentDeployed(IComponent component)
        {
            var activeVersion = GetActiveVersion(component);

            if (activeVersion != null) activeVersions.Remove(activeVersion);
        }

        private IActiveVersion GetActiveVersion(IVersion version)
        {
            return ActiveVersions.SingleOrDefault(x => x.Version.Equals(version));
        }

        private IActiveVersion GetActiveVersion(IComponent component)
        {
            return ActiveVersions.SingleOrDefault(x => x.Version.Component.Equals(component));
        }
    }
}
